﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.Statistics;
using SoftwareConsulting.BI11.NumericalMethods.Interfaces;

namespace SoftwareConsulting.BI11.NumericalMethods.Regression
{    
    /// <summary>
    /// Класс взвешенной точки с расчитанной ошибкой, используемой в тесте Хи-квадрат и МНК
    /// </summary>    
    public class WeightedPoint
    {
        private double _xValue;//x
        private double _yValue;//y
        private double _weight;//вес
        private double _error;//ошибка

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public WeightedPoint(double x, double y) : 
            this(x, y, 1)
        {
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="weight"></param>
        public WeightedPoint(double x, double y, double weight)
        {
            _xValue = x;
            _yValue = y;
            _weight = weight;
            _error = double.NaN;
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="x"></param>
        /// <param name="binContent">значение интервала гистограммы</param>
        public WeightedPoint(double x, int binContent) : 
            this(x, binContent, 1.0 / Math.Max(binContent, 1))
        {
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="x"></param>
        /// <param name="statisticalMoments"></param>
        public WeightedPoint(double x, StatisticalMoments statisticalMoments) : 
            this(x, statisticalMoments.Average)
        {
            Error = statisticalMoments.ErrorOnAverage;
        }
        
        /// <summary>
        /// Возвращает вклад в сумму Хи-квадрат от теоретической функции
        /// </summary>
        /// <param name="weightedPoint"></param>
        /// <returns></returns>
        public double GetChi2Contribution(WeightedPoint weightedPoint)
        {
            double residue = _yValue - weightedPoint.YValue;
            return residue * residue / (1 / weightedPoint.Weight + 1 / _weight);
        }

        /// <summary>
        /// Возвращает вклад в сумму Хи-квадрат от теоретической функции
        /// </summary>
        public double GetChi2Contribution(IOneVariableFunction function)
        {
            double residue = _yValue - function.GetValue(_xValue);
            return residue * residue * _weight;
        }

        /// <summary>
        /// Возвращает/устанавливает ошибку
        /// </summary>
        public double Error
        {
            get
            {
                if (double.IsNaN(_error))
                    _error = 1 / Math.Sqrt(_weight);
                return _error;
            }
            set
            {
                _error = value;
                _weight = 1 / (value * value);
            }
        }

        /// <summary>
        /// Возвращает как строку
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("({0},{1}+-{2})", _xValue, _yValue, Error);
        }

        /// <summary>
        /// Возвращает вес
        /// </summary>
        public double Weight
        {
            get 
            { 
                return _weight; 
            }
        }

        /// <summary>
        /// Возвращает X точки
        /// </summary>
        public double XValue
        {
            get 
            { 
                return _xValue; 
            }
        }

        /// <summary>
        /// Возвращает Y точки
        /// </summary>
        public double YValue
        {
            get 
            { 
                return _yValue; 
            }
        }
    }
}
